
// XortStudioView.cpp : implementation of the CXortStudioView class
//

#include "stdafx.h"
// SHARED_HANDLERS can be defined in an ATL project implementing preview, thumbnail
// and search filter handlers and allows sharing of document code with that project.
#ifndef SHARED_HANDLERS
#include "XortStudio.h"
#endif

#include "ProgressDlg.h"
#include "XortStudioDoc.h"
#include "CntrItem.h"
#include "resource.h"
#include "XortStudioView.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CXortStudioView

IMPLEMENT_DYNCREATE(CXortStudioView, CRichEditView)

BEGIN_MESSAGE_MAP(CXortStudioView, CRichEditView)
	ON_WM_DESTROY()
	// Standard printing commands
	ON_COMMAND(ID_FILE_PRINT, &CRichEditView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, &CRichEditView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CXortStudioView::OnFilePrintPreview)
	ON_WM_CONTEXTMENU()
	ON_WM_RBUTTONUP()
    ON_WM_PAINT()
    ON_WM_KEYUP()
    ON_MESSAGE(WM_APPEND_STRING,&CXortStudioView::OnAppendDisasmString)
END_MESSAGE_MAP()

// CXortStudioView construction/destruction

CXortStudioView::CXortStudioView()
{
    m_pDisassemblerThread = NULL;
    m_pProgressDlg = NULL;

    m_dwNativeThreadID = GetCurrentThreadId();

    cfBlue.cbSize = sizeof(cfBlue); cfBlue.dwMask = CFM_COLOR|CFM_FACE; cfBlue.dwEffects = ~CFE_AUTOCOLOR; cfBlue.crTextColor = RGB( 0, 0, 164 );
    strcpy( &cfBlue.szFaceName[0], "Courier New" );
    cfBlack.cbSize = sizeof(cfBlack); cfBlack.dwMask = CFM_COLOR|CFM_FACE; cfBlack.dwEffects = ~CFE_AUTOCOLOR; cfBlack.crTextColor = RGB( 64, 64, 64 );
    strcpy( &cfBlack.szFaceName[0], "Courier New" );
    cfGreen.cbSize = sizeof(cfGreen); cfGreen.dwMask = CFM_COLOR|CFM_FACE; cfGreen.dwEffects = ~CFE_AUTOCOLOR; cfGreen.crTextColor = RGB( 0, 124, 0 );
    strcpy( &cfGreen.szFaceName[0], "Courier New" );
    cfRed.cbSize = sizeof(cfRed); cfRed.dwMask = CFM_COLOR|CFM_FACE; cfRed.dwEffects = ~CFE_AUTOCOLOR; cfRed.crTextColor = RGB( 255, 0, 0 );
    strcpy( &cfRed.szFaceName[0], "Courier New" );

    m_dwDbgCursorIdx = -1;
}

CXortStudioView::~CXortStudioView()
{
}

BOOL CXortStudioView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	return CRichEditView::PreCreateWindow(cs);
}

void CXortStudioView::OnInitialUpdate()
{
	CRichEditView::OnInitialUpdate();

    CRichEditCtrl& ctrl = GetRichEditCtrl();

    // will do it directly by stream-in
    //CHARFORMAT2 cf;
    //cf.cbSize = sizeof(cf); cf.dwMask = CFM_COLOR|CFM_FACE; cf.dwEffects = ~CFE_AUTOCOLOR; cf.crTextColor = RGB( 0, 0, 0 );
    //strcpy( &cf.szFaceName[0], "Courier New" );
    //ctrl.SetDefaultCharFormat( cf );
    
    ctrl.SetReadOnly( TRUE );
    
    // Set the printing margins (720 twips = 1/2 inch)
	SetMargins(CRect(720, 720, 720, 720));
}

void CXortStudioView::OnPaint()
{
    CRichEditView::OnPaint();

    CClientDC dc( this );
    CRect rc;
    GetClientRect( rc );
    dc.MoveTo( 30, 0 );
    dc.LineTo( 30, rc.Height() );
}

// CXortStudioView printing
void CXortStudioView::OnFilePrintPreview()
{
#ifndef SHARED_HANDLERS
	AFXPrintPreview(this);
#endif
}

BOOL CXortStudioView::OnPreparePrinting(CPrintInfo* pInfo)
{
	// default preparation
	return DoPreparePrinting(pInfo);
}


void CXortStudioView::OnDestroy()
{
	// Deactivate the item on destruction; this is important
	// when a splitter view is being used
   COleClientItem* pActiveItem = GetDocument()->GetInPlaceActiveItem(this);
   if (pActiveItem != NULL && pActiveItem->GetActiveView() == this)
   {
      pActiveItem->Deactivate();
      ASSERT(GetDocument()->GetInPlaceActiveItem(this) == NULL);
   }
   CRichEditView::OnDestroy();
}


void CXortStudioView::OnRButtonUp(UINT /* nFlags */, CPoint point)
{
	ClientToScreen(&point);
	OnContextMenu(this, point);
}

void CXortStudioView::OnContextMenu(CWnd* /* pWnd */, CPoint point)
{
#ifndef SHARED_HANDLERS
	theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT, point.x, point.y, this, TRUE);
#endif
}


// CXortStudioView diagnostics

#ifdef _DEBUG
void CXortStudioView::AssertValid() const
{
	CRichEditView::AssertValid();
}

void CXortStudioView::Dump(CDumpContext& dc) const
{
	CRichEditView::Dump(dc);
}

CXortStudioDoc* CXortStudioView::GetDocument() const // non-debug version is inline
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CXortStudioDoc)));
	return (CXortStudioDoc*)m_pDocument;
}
#endif //_DEBUG


// CXortStudioView message handlers

void CXortStudioView::OnKeyUp( UINT nChar, UINT nRepCnt, UINT nFlags )
{
    TRACE( "OnKeyUp ->%d\n", nChar );
    CRichEditCtrl& ctrl = GetRichEditCtrl();

    DWORD dwCurrentLine = ctrl.LineFromChar( ctrl.LineIndex(-1) );
    BOOL bRet;
    
    switch( nChar )
    {
    case VK_F5:
        GetDocument()->DebugRun();
        break;
    case VK_F9: // set/remove breakpoint
        bRet = GetDocument()->SetBreakpoint( dwCurrentLine );
        UpdateBreakpointLine( dwCurrentLine, bRet );
        break;
    case VK_F10:
        GetDocument()->DebugStepOver();
        break;
    case VK_F11:
        GetDocument()->DebugStepInto();
        break;
    }
}

void CXortStudioView::ShowDisassembly( const CString& csDisAsm )
{
    if( m_pDisassemblerThread )
        return;
    m_csAsmRTF = csDisAsm;

    m_pDisassemblerThread = AfxBeginThread( &LoadDisassemblyThreadProc, this );
    
    CProgressDlg dlg;
    m_pProgressDlg = &dlg;
    dlg.DoModal();

    GetRichEditCtrl().SetSel( 0, 0 );
    GetRichEditCtrl().SetScrollPos( 0, 0 );

    TRACE( "Number of lines in document: %d\n", GetRichEditCtrl().GetLineCount() );
}

UINT CXortStudioView::LoadDisassemblyThreadProc( LPVOID pParam )
{
    Sleep( 500 );
    CXortStudioView *pView = (CXortStudioView*)pParam;
    pView->LoadDisassemblyIntoRichEdit();
    pView->m_pDisassemblerThread = NULL;
    return 0;
}

void CXortStudioView::LoadDisassemblyIntoRichEdit()
{
    EDITSTREAM editStream;
    editStream.dwCookie = (DWORD_PTR)this;
    editStream.dwError = -1;
    editStream.pfnCallback = &RichEditInStreamCBStatic;
    m_dwIdx = 0;
    GetRichEditCtrl().StreamIn( SF_RTF, editStream );
    m_dwIdx = 0;
    m_csAsmRTF.Empty();

    if( m_pProgressDlg )
    {
        m_pProgressDlg->CloseDialog();
        m_pProgressDlg = NULL;
    }

    //editStream.pfnCallback = &RichEditOutStreamCBStatic;
    //m_dwIdx = 0;
    //GetRichEditCtrl().StreamOut( SF_RTF, editStream );
}

DWORD __stdcall CXortStudioView::RichEditOutStreamCBStatic( DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb )
{
    CXortStudioView *pThis = (CXortStudioView *)dwCookie;
    return pThis->RichEditOutStreamCB( pbBuff, cb, pcb );
}

DWORD CXortStudioView::RichEditOutStreamCB( LPBYTE pbBuff, LONG cb, LONG *pcb )
{
    for( long i = 0; i < cb; i++ )
    {
        TRACE( "%c", (char)pbBuff[i] );
    }
    return 0;
}

DWORD __stdcall CXortStudioView::RichEditInStreamCBStatic( DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb )
{
    CXortStudioView *pThis = (CXortStudioView *)dwCookie;
    return pThis->RichEditInStreamCB( pbBuff, cb, pcb );
}

DWORD CXortStudioView::RichEditInStreamCB( LPBYTE pbBuff, LONG cb, LONG *pcb )
{
    LPCSTR lpszRTF = m_csAsmRTF.GetBuffer();
    LONG lLength = m_csAsmRTF.GetLength();
    LONG dwLen = lLength - m_dwIdx;
    DWORD dwPercent = m_dwIdx * 100 / lLength;
    static DWORD s_dwProgress = 0;

    if( m_pProgressDlg && s_dwProgress != dwPercent )
    {
        CString csFmt; csFmt.Format( "Loading: %d %%", dwPercent );
        m_pProgressDlg->SetTextMessage( csFmt );
        m_pProgressDlg->SetProgress( dwPercent );
        s_dwProgress = dwPercent;
    }

    if( dwLen == 0 )
    {
        *pcb = 0;
        return 0;
    }

    if( dwLen < cb )
    {
        memcpy( pbBuff, &lpszRTF[m_dwIdx], dwLen );
        m_dwIdx += dwLen;
        *pcb = dwLen;
    } else {
        memcpy( pbBuff, &lpszRTF[m_dwIdx], cb );
        m_dwIdx += cb;
        *pcb = cb;
    }

    return 0;
}

LRESULT CXortStudioView::OnAppendDisasmString( WPARAM wParam, LPARAM lParam )
{
    AppendString( (LPCSTR)wParam, (DWORD)lParam );
    free( (void*)wParam );
    return 0;
}

void CXortStudioView::AppendString( LPCSTR lpszText, DWORD dwColor )
{
    if( GetCurrentThreadId() != m_dwNativeThreadID )
    {
        this->PostMessage( WM_APPEND_STRING, (WPARAM)_strdup(lpszText), dwColor );
        Sleep( 6 );
        return;
    }

    CHARFORMAT2 *pCF;
    switch( dwColor )
    {
    default:
    case CLR_BLACK: pCF = &cfBlack; break;
    case CLR_RED:   pCF = &cfRed; break;
    case CLR_GREEN: pCF = &cfGreen; break;
    case CLR_BLUE:  pCF = &cfBlue; break;
    }

    CRichEditCtrl& ctrl = GetRichEditCtrl();
    long lStart, lEnd;
    
    ctrl.SetSel( -1, -1 ); // Set the cursor to the end of the text area and deselect everything.
    ctrl.ReplaceSel( lpszText ); // Inserts when nothing is selected.

    ctrl.GetSel( lStart, lEnd );
    lStart = lEnd - strlen( lpszText );
    ctrl.SetSel( lStart, -1 );
    ctrl.SetSelectionCharFormat( *pCF );
}

void CXortStudioView::LocateSymbol( symbolref pSymbol )
{
    CRichEditCtrl& ctrl = GetRichEditCtrl();

    FINDTEXTEX ft;
    ft.chrg.cpMin = 0;
    ft.chrg.cpMax = -1;
    ft.lpstrText = pSymbol->m_csName.GetBuffer();

    long iRet = ctrl.FindText( FR_MATCHCASE|FR_WHOLEWORD|FR_DOWN, &ft );
    if( iRet != -1 ) {
        ctrl.SetSel( ft.chrgText );
    }
}

void CXortStudioView::ScrollIntoView( DWORD dwLineIdx )
{
    CRichEditCtrl& ctrl = GetRichEditCtrl();
    long lStart, lEnd;

    ctrl.GetSel( lStart, lEnd );

    long lLineCur = ctrl.LineFromChar( lStart );
    long lLineNew = dwLineIdx;
    long lPos;

    lPos = lLineNew - lLineCur; 

    ctrl.LineScroll( lPos, 0 );
    int iCharPos = ctrl.LineIndex( lLineNew );

    //int iLen = ctrl.LineLength( lLineNew );

    ctrl.SetSel( iCharPos, iCharPos+2 );
}

void CXortStudioView::UpdateBreakpointLine( DWORD dwLineIdx, BOOL bHas )
{
    CRichEditCtrl& ctrl = GetRichEditCtrl();

    long lStart, lEnd;
    ctrl.GetSel( lStart, lEnd );

    int iCharPos = ctrl.LineIndex( dwLineIdx );
    ctrl.SetSel( iCharPos, iCharPos+1 );
    if( bHas ) {
        ctrl.SetSelectionCharFormat( cfRed );
        ctrl.ReplaceSel( "\a" );
    } else {
        ctrl.ReplaceSel( " " );
    }

    ctrl.SetSel( lStart, lEnd );
}

void CXortStudioView::MoveDebugCursorToLine( DWORD dwLineIdx )
{
    CRichEditCtrl& ctrl = GetRichEditCtrl();

    long lStart, lEnd;
    ctrl.GetSel( lStart, lEnd );

    int iCharPos;
    
    if( m_dwDbgCursorIdx != -1 )
    {
        iCharPos = ctrl.LineIndex( m_dwDbgCursorIdx );
        ctrl.SetSel( iCharPos+1, iCharPos+2 );
        ctrl.ReplaceSel( " " );
    }

    if( dwLineIdx != -1 )
    {
        iCharPos = ctrl.LineIndex( dwLineIdx );
        ctrl.SetSel( iCharPos+1, iCharPos+2 );
        ctrl.SetSelectionCharFormat( cfRed );
        ctrl.ReplaceSel( ">" );
    }

    m_dwDbgCursorIdx = dwLineIdx;

    ctrl.SetSel( lStart, lEnd );

    ScrollIntoView( m_dwDbgCursorIdx );
}